Viper lets you define hot keys, i.e., you can associate keyboard keys such as F1, Help, PgDn, etc., with Emacs Lisp functions (that may already exist or that you will write). Each key has a "preferred form" in Emacs. For instance, the Up key's preferred form is [up], the Help key's preferred form is [help], and the Undo key has the preferred form [f14]. You can find out the preferred form of a key by typing M-x describe-key-briefly and then typing the key you want to know about.
Under the X Window System, every keyboard key emits its preferred form, so you can just type
(global-set-key [f11] 'calendar) ; L1, Stop
(global-set-key [f14] 'undo) ; L4, Undo
to bind L1 (a key that exists on some SUN
workstations) so it will invoke the Emacs Calendar and to bind L4
so it will undo changes. However, on a dumb terminal or in an
Xterm window, even the standard arrow keys may not emit the right
signals for Emacs to understand. To let Emacs know about those
keys, you will have to find out which key sequences they emit by
typing C-q and then the key (you should switch to
Emacs state first). Then you can bind those sequences to their
preferred forms using input-decode-map as
follows:
(cond ((string= (getenv "TERM") "xterm")
(define-key input-decode-map "\e[192z" [f11]) ; L1
(define-key input-decode-map "\e[195z" [f14]) ; L4, Undo
The above illustrates how to do this for Xterm. On VT100, you would have to replace "xterm" with "vt100" and also change the key sequences (the same key may emit different sequences on different types of terminals).
The above keys are global, so they are overwritten by the local maps defined by the major modes and by Viper itself. Therefore, if you wish to change a binding set by a major mode or by Viper, read this.
Viper users who wish to specify their own key bindings should
be concerned only with the following three keymaps:
viper-vi-global-user-map for Vi state commands,
viper-insert-global-user-map for Insert state
commands, and viper-emacs-global-user-map for Emacs
state commands (note: customized bindings for Emacs state made to
viper-emacs-global-user-map are not
inherited by Insert state).
For more information on Viper keymaps, see the header of the
file viper.el. If you wish
to change a Viper binding, you can use the
define-key command, to modify
viper-vi-global-user-map,
viper-insert-global-user-map, and
viper-emacs-global-user-map, as explained below.
Each of these key maps affects the corresponding Viper state. The
keymap viper-insert-global-user-map also affects
Viper's Replace state.
If you want to bind a key, say C-v, to the function that scrolls page down and to make 0 display information on the current buffer, putting this in .viper will do the trick in Vi state:
(define-key viper-vi-global-user-map "\C-v" 'scroll-down)
To set a key globally,
(define-key viper-emacs-global-user-map "\C-c m" 'smail)
(define-key viper-vi-global-user-map "0" 'viper-info-on-file)
Note, however, that this binding may be overwritten by other keymaps, since the global keymap has the lowest priority. To make sure that nothing will override a binding in Emacs state, you can write this:
(define-key viper-emacs-global-user-map "\C-c m" 'smail)
To customize the binding for C-h in Insert state:
(define-key viper-insert-global-user-map "\C-h" 'my-del-backwards-function)
Each Emacs command key calls some Lisp function. If you have enabled the Help, (see Rudimentary Changes) C-h k will show you the function for each specific key; C-h b will show all bindings, and C-h m will provide information on the major mode in effect. If Help is not enabled, you can still get help in Vi state by prefixing the above commands with \, e.g., \ C-h k (or you can use the Help menu in the menu bar, if Emacs runs under X).
Viper users can also change bindings on a per major mode
basis. As with global bindings, this can be done separately for
each of the three main Viper states. To this end, Viper provides
the function viper-modify-major-mode.
To modify keys in Emacs state for
my-favorite-major-mode, the user needs to create a
sparse keymap, say, my-fancy-map, bind whatever keys
necessary in that keymap, and put
(viper-modify-major-mode 'dired-mode 'emacs-state my-fancy-map)
in ~/.viper. To do the same in Vi and Insert
states, you should use vi-state and
insert-state. Changes in Insert state are also in
effect in Replace state. For instance, suppose that the user
wants to use dd in Vi state under Dired mode to delete
files, u to unmark files, etc. The following code in
~/.viper will then do the
job:
(setq my-dired-modifier-map (make-sparse-keymap))
(define-key my-dired-modifier-map "dd" 'dired-flag-file-deletion)
(define-key my-dired-modifier-map "u" 'dired-unmark)
(viper-modify-major-mode 'dired-mode 'vi-state my-dired-modifier-map)
A Vi purist may want to modify Emacs state under Dired mode so that k, l, etc., will move around in directory buffers, as in Vi. Although this is not recommended, as these keys are bound to useful Dired functions, the trick can be accomplished via the following code:
(setq my-dired-vi-purist-map (make-sparse-keymap))
(define-key my-dired-vi-purist-map "k" 'viper-previous-line)
(define-key my-dired-vi-purist-map "l" 'viper-forward-char)
(viper-modify-major-mode 'dired-mode 'emacs-state my-dired-vi-purist-map)
Yet another way to customize key bindings in a major mode is
to edit the list viper-major-mode-modifier-list
using the customization widget.
(This variable is in the Viper-misc customization group.) The
elements of this list are triples of the form: (major-mode
viper-state keymap), where the keymap contains bindings that are
supposed to be active in the given major mode and the given
viper-state.
Effects similar to key binding changes can be achieved by defining Vi keyboard macros using the Ex commands :map and :map!. The difference is that multi-key Vi macros do not override the keys they are bound to, unless these keys are typed in quick succession. So, with macros, one can use the normal keys alongside with the macros. If per-mode modifications are needed, the user can try both ways and see which one is more convenient. See Vi Macros, for details.
Note: in major modes that come up in Emacs state by
default, the aforesaid modifications may not take place
immediately (but only after the buffer switches to some other
Viper state and then back to Emacs state). To avoid this, one
should add viper-change-state-to-emacs to an
appropriate hook of that major mode. (Check the function
viper-set-hooks in viper.el for examples.) However, if you did
not set viper-always to nil, chances
are that you won't need to perform the above procedure, because
Viper will take care of most useful defaults.
Finally, Viper has a facility that lets the user define
per-buffer bindings, i.e., bindings that are in effect in some
specific buffers only. Unlike per-mode bindings described above,
per-buffer bindings can be defined based on considerations other
than the major mode. This is done via the function
viper-add-local-keys, which lets one specify
bindings that should be in effect in the current buffer only and
for a specific Viper state. For instance,
(viper-add-local-keys 'vi-state '(("ZZ" . TeX-command-master)
("ZQ" . viper-save-kill-buffer)))
redefines ZZ to invoke
TeX-command-master in vi-state and
ZQ to save-then-kill the current buffer. These
bindings take effect only in the buffer where this command is
executed. The typical use of this function is to execute the
above expression from within a function that is included in a
hook to some major mode. For instance, the above expression could
be called from a function, my-tex-init, which may be
added to tex-mode-hook as follows:
(add-hook 'tex-mode-hook 'my-tex-init)
When TeX mode starts, the hook is executed and the above Lisp expression is evaluated. Then, the bindings for ZZ and ZQ are changed in Vi command mode for all buffers in TeX mode.
Another useful application is to bind ZZ to
send-mail in the Mail mode buffers (the specifics of
this depend on which mail package you are using,
rmail, mh-e, vm, etc. For
instance, here is how to do this for mh-e, the Emacs
interface to MH:
(defun mh-add-vi-keys ()
"Set up ZZ for MH-e and XMH."
(viper-add-local-keys 'vi-state '(("ZZ" . mh-send-letter))))
(add-hook 'mh-letter-mode-hook 'mh-add-vi-keys)
You can also use viper-add-local-keys to set per
buffer bindings in Insert state and Emacs state by passing as a
parameter the symbols insert-state and
emacs-state, respectively. As with global bindings,
customized local bindings done to Emacs state are not inherited
by Insert state.
On rare occasions, local keys may be added by mistake. Usually
this is done indirectly, by invoking a major mode that adds local
keys (e.g., shell-mode redefines <RET>). In
such a case, exiting the wrong major mode won't rid you from
unwanted local keys, since these keys are local to Viper state
and the current buffer, not to the major mode. In such
situations, the remedy is to type M-x
viper-zap-local-keys.
So much about Viper-specific bindings. See Customization, and the Emacs quick reference card for the general info on key bindings in Emacs.